/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stud.cp.user.dbaccess;

import java.math.BigInteger;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import stud.cp.core.dbaccess.DAO;
import stud.cp.core.dbaccess.DAOException;
import stud.cp.user.valueobjects.DBObject;

/**
 * Класс, описывающий доступ к таблице Object
 *
 * @author Morfant
 */
public class ObjectDAO implements DAO<DBObject> {

    /**
     * Логгер.
     */
    private static Logger logger = Logger.getLogger("CPTNP");
    /**
     * Имя колонок таблицы object
     */
    private static String[] objectColumnName = {"object_id", "name", "description", "object_type_id"};
    /**
     * Список названий колонок таблицы object_types.
     */
    private static String[] objectTypesColumnName = {"object_type_id", "object_type_name", "description"};
    /**
     * Коннект к базе данных.
     */
    private Connection connection = null;

    /**
     * Конструктор, который устанавливает коннект к базе данных.
     *
     * @param connection Связь с базой данных.
     */
    public ObjectDAO(Connection connection) {
        this.connection = connection;
    }

    /**
     * Запрос, позволяющий получить запись по id.
     *
     * @param id ID
     * @return Запрос.
     */
    private String getQueryForGetById(BigInteger id) {
        StringBuilder query = new StringBuilder();

        query.append("select w.object_id,w.name,w.description,w.object_type_id,wt.name as \"object_type_name\"\n");
        query.append("from objects w \n");
        query.append("inner join object_types wt on w.object_type_id=wt.object_type_id \n");
        query.append("where object_id=");
        query.append(id);

        return query.toString();
    }

    /**
     * Метод, позволяющий получить запись по её ID
     *
     * @param id ID
     * @return Данные записи.
     * @throws DAOException <p> Ошикба доступа к данным. </p>
     */
    @Override
    public DBObject getById(BigInteger id) throws DAOException {
        DBObject object = null;
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForGetById(id));
            set = prestat.executeQuery();
            if (set.next()) {
                object = new DBObject(set.getBigDecimal(objectColumnName[0]).toBigInteger(),
                        set.getBigDecimal(objectColumnName[3]).toBigInteger(),
                        set.getString(objectColumnName[1]),
                        set.getString(objectColumnName[2]),
                        set.getString(objectTypesColumnName[1]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety", ex);
            throw new DAOException("Ошибка при создание данных..", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return object;
    }

    /**
     * Запрос на обновление записи в таблице.
     *
     * @param id ID
     * @param name Имя
     * @return Запрос.
     */
    private String getQueryForUpdate(BigInteger object_id, String name, String description) {
        StringBuilder query = new StringBuilder();
        query.append("UPDATE objects \n");
        query.append("SET name='");
        query.append(name);
        query.append("',");
        query.append("description='");
        query.append(description);
        query.append("'\n");
        query.append("WHERE object_id=");
        query.append(object_id);
        return query.toString();
    }

    /**
     * Метод, отвечающий за обновление данных.
     *
     * @param entry Запись.
     * @throws DAOException Ошибка при обновление данных.
     */
    @Override
    public void update(DBObject entry) throws DAOException {
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(
                    getQueryForUpdate(
                    entry.getObject_id(),
                    entry.getName(),
                    entry.getDescription()));
            prestat.executeUpdate();
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка обновлени данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }

    /**
     * Запрос на добавление новой записи в таблицу.
     *
     * @param id ID
     * @param name Имя
     * @return Запрос.
     */
    private String getQueryForInsert(BigInteger object_id, BigInteger object_type_id, String name, String description) {
        StringBuilder query = new StringBuilder();
        query.append("insert into objects(object_id,object_type_id,name,description) \n");
        query.append("VALUES(");
        query.append(object_id);
        query.append(",");
        query.append(object_type_id);
        query.append(",'");
        query.append(name);
        query.append("','");
        query.append(description);
        query.append("')");
        return query.toString();
    }

    /**
     * Зарпос на получения максимального id из таблицы.
     *
     * @return Зарпос.
     */
    private String getQueryForSelectMaxId() {
        StringBuilder query = new StringBuilder();
        query.append("SELECT MAX(object_id) \n");
        query.append("FROM objects");
        return query.toString();
    }

    /**
     * Метод, который создает новую запись в таблице.
     *
     * @param entry Запись.
     * @throws DAOException Ошибка создания новой записи.
     */
    @Override
    public BigInteger create(DBObject entry) throws DAOException {
        ResultSet set = null;
        PreparedStatement prestat = null;
        CallableStatement proc;
        try {
            proc = connection.prepareCall("{ call GetNextIdentity(?,?)}");
            proc.setString(1, "objects");
            proc.registerOutParameter(2,  java.sql.Types.BIGINT);

            proc.execute();

            BigInteger index = proc.getBigDecimal(2).toBigInteger();

            proc.close();

            prestat = connection.prepareStatement(getQueryForInsert(index,
                    entry.getObject_type_id(),
                    entry.getName(),
                    entry.getDescription()));
            prestat.executeUpdate();
            //TODO::[High]Delete string
            entry.setWeaon_id(index);
            return index;
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка при создание записи.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }

    @Override
    public List<DBObject> getAll() throws DAOException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Запрос, позволяющий при выполнении получить все оружие, которое
     * принадлежит определенному типу оружия.
     *
     * @param object_type_id ID типа оружия
     * @return Строка запроса.
     */
    private String getQueryForAllObjectForObjectType(BigInteger object_type_id) {
        StringBuilder query = new StringBuilder();
        query.append("select w.object_id,w.name,w.description,w.object_type_id,wt.name as \"object_type_name\"\n");
        query.append("from objects w \n");
        query.append("inner join object_types wt on w.object_type_id=wt.object_type_id \n");
        query.append("where wt.object_type_id=");
        query.append(object_type_id);
        return query.toString();
    }

    /**
     * Метод, который возращает список оружия принадлежащий определенному типу
     * оружия.
     *
     * @param object_type_id ID типа оружия.
     * @return Список данных.
     */
    public List<DBObject> getAllObjectByObjectType(BigInteger object_type_id) throws DAOException {
        List<DBObject> data_list = new LinkedList<DBObject>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForAllObjectForObjectType(object_type_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new DBObject(set.getBigDecimal(objectColumnName[0]).toBigInteger(),
                        set.getBigDecimal(objectColumnName[3]).toBigInteger(),
                        set.getString(objectColumnName[1]),
                        set.getString(objectColumnName[2]),
                        set.getString(objectTypesColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, позволяющий при выполнении получить все оружие, которое
     * принадлежит определенному типу оружия.
     *
     * @param object_type_id ID типа оружия
     * @return Строка запроса.
     */
    private String getQueryForAllObjectForObjectTypeExcludeCurrentObject(BigInteger object_type_id, BigInteger object_id) {
        StringBuilder query = new StringBuilder();
        query.append("select w.object_id,w.name,w.description,w.object_type_id,wt.name as \"object_type_name\"\n");
        query.append("from objects w \n");
        query.append("inner join object_types wt on w.object_type_id=wt.object_type_id \n");
        query.append("where wt.object_type_id=");
        query.append(object_type_id);
        query.append("and w.object_id<>");
        query.append(object_id);
        return query.toString();
    }

    /**
     * Метод, который возращает список оружия принадлежащий определенному типу
     * оружия, исключая из него оружие, которое запрашивается пользователем.
     *
     * @param object_type_id ID типа оружия.
     * @param object_id ID оружия, которое запрашивает список ссылок.
     * @return Список данных.
     */
    public List<DBObject> getAllObjectByObjectTypeExcludeObject(BigInteger object_type_id, BigInteger object_id) throws DAOException {
        List<DBObject> data_list = new LinkedList<DBObject>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat =
                    connection.prepareStatement(
                    getQueryForAllObjectForObjectTypeExcludeCurrentObject(object_type_id, object_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new DBObject(set.getBigDecimal(objectColumnName[0]).toBigInteger(),
                        set.getBigDecimal(objectColumnName[3]).toBigInteger(),
                        set.getString(objectColumnName[1]),
                        set.getString(objectColumnName[2]),
                        set.getString(objectTypesColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Зарпос на удаление из таблицы записи по id.
     *
     * @param id ID
     * @return Зарпос.
     */
    private String getQueryForDelete(BigInteger id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM objects \n");
        query.append("WHERE object_id=");
        query.append(id);
        return query.toString();
    }

    /**
     * Удаление из табилцы property_values.
     *
     * @param object_id ID оружия.
     * @return Зарпос.
     */
    private String getQueryForDeleteFromParameters(BigInteger object_id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM parameters \n");
        query.append("WHERE object_id=");
        query.append(object_id);
        return query.toString();
    }

    /**
     * Удаление из табилцы object_references.
     *
     * @param object_id ID оружия.
     * @return Зарпос.
     */
    private String getQueryForDeleteFromObjectReferences(BigInteger object_id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM object_references \n");
        query.append("WHERE object_id=");
        query.append(object_id);
        query.append("\n OR reference_object_id=");
        query.append(object_id);
        return query.toString();
    }

    /**
     * Удаление истории оружия.
     *
     * @param id ID оружия.
     * @return Запрос.
     */
    private String getQueryForDeleteObjectHistory(BigInteger id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM history \n");
        query.append("WHERE object_id=");
        query.append(id);
        return query.toString();
    }

    /**
     * Операция удаления записи из таблицы.
     *
     * @param id Объект, который надо удалить.
     */
    @Override
    public void delete(BigInteger id) throws DAOException {
        PreparedStatement prestat = null;
        try {
//            prestat = connection.prepareStatement(getQueryForDeleteObjectHistory(id));
//            prestat.executeUpdate();
//            prestat.close();
            prestat = connection.prepareStatement(getQueryForDeleteFromParameters(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDeleteFromObjectReferences(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDelete(id));
            prestat.executeUpdate();
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка удаления данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }
}
